package at.fhooe.mc;

import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.easymock.EasyMock;
import org.easymock.IMocksControl;
import org.junit.Before;
import org.junit.Test;
import at.fhooe.mc.control.ElevatorControlCenter;
import at.fhooe.mc.control.IAdapter;
import at.fhooe.mc.control.automation.IAutoAlgorithm;
import at.fhooe.mc.control.exception.IndexChangeException;
import at.fhooe.mc.data.ControlMode;
import at.fhooe.mc.data.DirectionType;
import at.fhooe.mc.data.DoorState;
import at.fhooe.mc.data.ElevatorData;
import at.fhooe.mc.data.FloorData;
import at.fhooe.mc.data.SystemData;
import at.fhooe.mc.data.control.ElevatorControlData;
import at.fhooe.mc.model.ElevatorControlCenterObserver;

public class ElevatorControlReflactionTest
{

	private ElevatorControlCenter ecc;
	private Method checkIfThereAreChanges;

	private IAdapter mock;
	private IMocksControl ctrl;

	private ElevatorControlCenterObserver mockObserver;
	private IAutoAlgorithm mockAutoAlgorithm;

	private SystemData curSystemData;
	private List<ElevatorControlData> curControlData;

	private List<ElevatorControlData> getDefaultControlData()
	{
		List<ElevatorControlData> controlData = new ArrayList<ElevatorControlData>();

		ElevatorControlData elevControl = new ElevatorControlData();
		elevControl.setMode(ControlMode.AUTOMATIC);
		elevControl.setNewTarget(2);
		controlData.add(elevControl);

		elevControl = new ElevatorControlData();
		elevControl.setMode(ControlMode.MANUAL);
		elevControl.setNewTarget(3);
		controlData.add(elevControl);

		return controlData;
	}

	private SystemData getDefaultSystemData()
	{
		SystemData defaultData = new SystemData();
		defaultData.setClockTick(1000L);
		defaultData.setFloorHeight(20);

		FloorData f = new FloorData();
		f.setFloorIndex(0);
		f.setFloorButtonDown(false);
		f.setFloorButtonUp(true);
		defaultData.getFloors().add(f);

		f = new FloorData();
		f.setFloorIndex(1);
		f.setFloorButtonDown(true);
		f.setFloorButtonUp(false);
		defaultData.getFloors().add(f);

		f = new FloorData();
		f.setFloorIndex(2);
		f.setFloorButtonDown(true);
		f.setFloorButtonUp(true);
		defaultData.getFloors().add(f);

		f = new FloorData();
		f.setFloorIndex(3);
		f.setFloorButtonDown(false);
		f.setFloorButtonUp(false);
		defaultData.getFloors().add(f);

		ElevatorData defaultElevator = new ElevatorData(4,
			ControlMode.AUTOMATIC);
		defaultElevator.setElevatorIndex(0);
		defaultElevator.setElevatorFloor(2);
		defaultElevator.setElevatorPosition(10);
		defaultElevator.setCurrentTarget(3);
		defaultElevator.setCommittedDirection(DirectionType.UP);
		defaultElevator.setElevatorDoorStatus(DoorState.OPEN);
		defaultElevator.setElevatorSpeed(23);
		defaultElevator.setElevatorAccel(12);
		defaultElevator.setElevatorCapacity(200);
		defaultElevator.setElevatorWeight(100);

		defaultElevator.getElevatorButton()[0] = true;
		defaultElevator.getElevatorButton()[1] = false;
		defaultElevator.getElevatorButton()[2] = true;
		defaultElevator.getElevatorButton()[3] = false;

		defaultElevator.getServicesFloors()[0] = false;
		defaultElevator.getServicesFloors()[1] = false;
		defaultElevator.getServicesFloors()[2] = true;
		defaultElevator.getServicesFloors()[3] = true;

		defaultData.getElevators().add(defaultElevator);

		ElevatorData e = new ElevatorData(4, ControlMode.MANUAL);
		e.setElevatorIndex(1);
		e.setElevatorFloor(1);
		e.setElevatorPosition(2);
		e.setCurrentTarget(0);
		e.setCommittedDirection(DirectionType.DOWN);
		e.setElevatorDoorStatus(DoorState.OPENING);
		e.setElevatorSpeed(0);
		e.setElevatorAccel(-1);
		e.setElevatorCapacity(220);
		e.setElevatorWeight(90);

		e.getElevatorButton()[0] = false;
		e.getElevatorButton()[1] = true;
		e.getElevatorButton()[2] = false;
		e.getElevatorButton()[3] = true;

		e.getServicesFloors()[0] = true;
		e.getServicesFloors()[1] = true;
		e.getServicesFloors()[2] = false;
		e.getServicesFloors()[3] = false;

		defaultData.getElevators().add(e);

		return defaultData;
	}

	@Before
	public void setup() throws SecurityException, NoSuchFieldException,
		IllegalArgumentException, IllegalAccessException, NoSuchMethodException
	{
		ecc = new ElevatorControlCenter();

		Field fieldData = ElevatorControlCenter.class.getDeclaredField("data");
		fieldData.setAccessible(true);
		curSystemData = getDefaultSystemData();
		fieldData.set(ecc, curSystemData);

		Field fieldControlData = ElevatorControlCenter.class.getDeclaredField("controlElevatorData");
		fieldControlData.setAccessible(true);
		curControlData = getDefaultControlData();
		fieldControlData.set(ecc, curControlData);

		checkIfThereAreChanges = ElevatorControlCenter.class.getDeclaredMethod(
			"checkIfThereAreChanges", SystemData.class);
		checkIfThereAreChanges.setAccessible(true);

		ctrl = EasyMock.createStrictControl();
		mock = ctrl.createMock(IAdapter.class);

		mockObserver = ctrl.createMock(ElevatorControlCenterObserver.class);

		mockAutoAlgorithm = ctrl.createMock(IAutoAlgorithm.class);

		ecc.setAdapter(mock);
		ecc.setAutoAlgorithm(mockAutoAlgorithm);
	}

	@Test
	public void testCheckIfThereAreChangesNoChanges()
		throws IllegalArgumentException, IllegalAccessException,
		InvocationTargetException
	{
		assertEquals(Boolean.FALSE,
			checkIfThereAreChanges.invoke(ecc, getDefaultSystemData()));
	}

	@Test
	public void testCheckIfThereAreChangesClockTick()
		throws IllegalArgumentException, IllegalAccessException,
		InvocationTargetException
	{
		SystemData data = getDefaultSystemData();
		data.setClockTick(200);
		assertEquals(Boolean.FALSE, checkIfThereAreChanges.invoke(ecc, data));
	}

	@Test
	public void testCheckIfThereAreChangesSameClockTick()
		throws IllegalArgumentException, IllegalAccessException,
		InvocationTargetException
	{
		SystemData data = getDefaultSystemData();
		data.setFloorHeight(2000);
		assertEquals(Boolean.FALSE, checkIfThereAreChanges.invoke(ecc, data));
	}

	@Test
	public void testCheckIfThereAreChangesFloorHeight()
		throws IllegalArgumentException, IllegalAccessException,
		InvocationTargetException
	{
		SystemData data = getDefaultSystemData();
		data.setClockTick(200);
		data.setFloorHeight(1);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));
	}

	@Test
	public void testCheckIfThereAreChangesFloorIndex()
		throws IllegalArgumentException, IllegalAccessException
	{
		SystemData data = getDefaultSystemData();
		data.setClockTick(200);
		data.getFloors().get(0).setFloorIndex(3);

		try
		{
			checkIfThereAreChanges.invoke(ecc, data);
			fail("IndexChangeException expected");
		}
		catch (InvocationTargetException e)
		{
			Throwable t = e.getCause();
			assertEquals(IndexChangeException.class, t.getClass());
		}
	}

	@Test
	public void testCheckIfThereAreChangesRemovedFloor()
		throws IllegalArgumentException, IllegalAccessException
	{
		SystemData data = getDefaultSystemData();
		data.setClockTick(200);
		data.getFloors().remove(3);

		try
		{
			checkIfThereAreChanges.invoke(ecc, data);
			fail("IndexChangeException expected");
		}
		catch (InvocationTargetException e)
		{
			Throwable t = e.getCause();
			assertEquals(IndexOutOfBoundsException.class, t.getClass());
		}
	}

	@Test
	public void testCheckIfThereAreChangesFloorButtonDown()
		throws IllegalArgumentException, IllegalAccessException,
		InvocationTargetException
	{
		SystemData data = getDefaultSystemData();
		data.setClockTick(200);
		data.getFloors().get(0).setFloorButtonDown(true);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getFloors().get(1).setFloorButtonDown(false);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getFloors().get(2).setFloorButtonDown(false);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getFloors().get(3).setFloorButtonDown(true);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));
	}

	@Test
	public void testCheckIfThereAreChangesFloorButtonUp()
		throws IllegalArgumentException, IllegalAccessException,
		InvocationTargetException
	{
		SystemData data = getDefaultSystemData();
		data.setClockTick(200);
		data.getFloors().get(0).setFloorButtonUp(false);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getFloors().get(1).setFloorButtonUp(true);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getFloors().get(2).setFloorButtonUp(false);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getFloors().get(3).setFloorButtonUp(true);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));
	}

	@Test
	public void testCheckIfThereAreChangesElevatorIndex()
		throws IllegalArgumentException, IllegalAccessException
	{
		SystemData data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().get(0).setElevatorIndex(6);

		try
		{
			checkIfThereAreChanges.invoke(ecc, data);
			fail("IndexChangeException expected");
		}
		catch (InvocationTargetException e)
		{
			Throwable t = e.getCause();
			assertEquals(IndexChangeException.class, t.getClass());
		}
	}

	@Test
	public void testCheckIfThereAreChangesRemovedElevator()
		throws IllegalArgumentException, IllegalAccessException
	{
		SystemData data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().remove(1);

		try
		{
			checkIfThereAreChanges.invoke(ecc, data);
			fail("IndexChangeException expected");
		}
		catch (InvocationTargetException e)
		{
			Throwable t = e.getCause();
			assertEquals(IndexOutOfBoundsException.class, t.getClass());
		}
	}

	@Test
	public void testCheckIfThereAreChangesElevatorFull()
		throws IllegalArgumentException, IllegalAccessException,
		InvocationTargetException
	{
		SystemData data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().get(0).setCommittedDirection(DirectionType.DOWN);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().get(0).setCurrentTarget(2);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().get(0).setElevatorAccel(4);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().get(0).setElevatorCapacity(2);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().get(0).setElevatorDoorStatus(DoorState.CLOSING);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().get(0).setElevatorFloor(0);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().get(0).setElevatorPosition(4);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().get(0).setElevatorSpeed(8);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().get(0).setElevatorWeight(40);
		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().remove(0);

		ElevatorData defaultElevator = new ElevatorData(4, ControlMode.MANUAL);
		defaultElevator.setElevatorIndex(0);
		defaultElevator.setElevatorFloor(2);
		defaultElevator.setElevatorPosition(10);
		defaultElevator.setCurrentTarget(3);
		defaultElevator.setCommittedDirection(DirectionType.UP);
		defaultElevator.setElevatorDoorStatus(DoorState.OPEN);
		defaultElevator.setElevatorSpeed(23);
		defaultElevator.setElevatorAccel(12);
		defaultElevator.setElevatorCapacity(200);
		defaultElevator.setElevatorWeight(100);

		defaultElevator.getElevatorButton()[0] = true;
		defaultElevator.getElevatorButton()[1] = false;
		defaultElevator.getElevatorButton()[2] = true;
		defaultElevator.getElevatorButton()[3] = false;

		defaultElevator.getServicesFloors()[0] = false;
		defaultElevator.getServicesFloors()[1] = false;
		defaultElevator.getServicesFloors()[2] = true;
		defaultElevator.getServicesFloors()[3] = true;

		data.getElevators().set(0, defaultElevator);

		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().remove(0);

		defaultElevator = new ElevatorData(4, ControlMode.AUTOMATIC);
		defaultElevator.setElevatorIndex(0);
		defaultElevator.setElevatorFloor(2);
		defaultElevator.setElevatorPosition(10);
		defaultElevator.setCurrentTarget(3);
		defaultElevator.setCommittedDirection(DirectionType.UP);
		defaultElevator.setElevatorDoorStatus(DoorState.OPEN);
		defaultElevator.setElevatorSpeed(23);
		defaultElevator.setElevatorAccel(12);
		defaultElevator.setElevatorCapacity(200);
		defaultElevator.setElevatorWeight(100);

		// changed
		defaultElevator.getElevatorButton()[0] = false;
		defaultElevator.getElevatorButton()[1] = false;
		defaultElevator.getElevatorButton()[2] = true;
		defaultElevator.getElevatorButton()[3] = false;

		defaultElevator.getServicesFloors()[0] = false;
		defaultElevator.getServicesFloors()[1] = false;
		defaultElevator.getServicesFloors()[2] = true;
		defaultElevator.getServicesFloors()[3] = true;

		data.getElevators().set(0, defaultElevator);

		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().remove(0);

		defaultElevator = new ElevatorData(4, ControlMode.AUTOMATIC);
		defaultElevator.setElevatorIndex(0);
		defaultElevator.setElevatorFloor(2);
		defaultElevator.setElevatorPosition(10);
		defaultElevator.setCurrentTarget(3);
		defaultElevator.setCommittedDirection(DirectionType.UP);
		defaultElevator.setElevatorDoorStatus(DoorState.OPEN);
		defaultElevator.setElevatorSpeed(23);
		defaultElevator.setElevatorAccel(12);
		defaultElevator.setElevatorCapacity(200);
		defaultElevator.setElevatorWeight(100);

		defaultElevator.getElevatorButton()[0] = true;
		// changed
		defaultElevator.getElevatorButton()[1] = true;
		defaultElevator.getElevatorButton()[2] = true;
		defaultElevator.getElevatorButton()[3] = false;

		defaultElevator.getServicesFloors()[0] = false;
		defaultElevator.getServicesFloors()[1] = false;
		defaultElevator.getServicesFloors()[2] = true;
		defaultElevator.getServicesFloors()[3] = true;

		data.getElevators().set(0, defaultElevator);

		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().remove(0);

		defaultElevator = new ElevatorData(4, ControlMode.AUTOMATIC);
		defaultElevator.setElevatorIndex(0);
		defaultElevator.setElevatorFloor(2);
		defaultElevator.setElevatorPosition(10);
		defaultElevator.setCurrentTarget(3);
		defaultElevator.setCommittedDirection(DirectionType.UP);
		defaultElevator.setElevatorDoorStatus(DoorState.OPEN);
		defaultElevator.setElevatorSpeed(23);
		defaultElevator.setElevatorAccel(12);
		defaultElevator.setElevatorCapacity(200);
		defaultElevator.setElevatorWeight(100);

		defaultElevator.getElevatorButton()[0] = true;
		defaultElevator.getElevatorButton()[1] = false;
		defaultElevator.getElevatorButton()[2] = true;
		defaultElevator.getElevatorButton()[3] = false;

		// changed
		defaultElevator.getServicesFloors()[0] = true;
		defaultElevator.getServicesFloors()[1] = false;
		defaultElevator.getServicesFloors()[2] = true;
		defaultElevator.getServicesFloors()[3] = true;

		data.getElevators().set(0, defaultElevator);

		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));

		data = getDefaultSystemData();
		data.setClockTick(200);
		data.getElevators().remove(0);

		defaultElevator = new ElevatorData(4, ControlMode.AUTOMATIC);
		defaultElevator.setElevatorIndex(0);
		defaultElevator.setElevatorFloor(2);
		defaultElevator.setElevatorPosition(10);
		defaultElevator.setCurrentTarget(3);
		defaultElevator.setCommittedDirection(DirectionType.UP);
		defaultElevator.setElevatorDoorStatus(DoorState.OPEN);
		defaultElevator.setElevatorSpeed(23);
		defaultElevator.setElevatorAccel(12);
		defaultElevator.setElevatorCapacity(200);
		defaultElevator.setElevatorWeight(100);

		defaultElevator.getElevatorButton()[0] = true;
		defaultElevator.getElevatorButton()[1] = false;
		defaultElevator.getElevatorButton()[2] = true;
		defaultElevator.getElevatorButton()[3] = false;

		defaultElevator.getServicesFloors()[0] = false;
		defaultElevator.getServicesFloors()[1] = false;
		// changed
		defaultElevator.getServicesFloors()[2] = false;
		defaultElevator.getServicesFloors()[3] = true;

		data.getElevators().set(0, defaultElevator);

		assertEquals(Boolean.TRUE, checkIfThereAreChanges.invoke(ecc, data));
	}

	@Test
	public void testCheckIfThereAreChangesNullParameter()
		throws IllegalArgumentException, IllegalAccessException,
		InvocationTargetException
	{
		assertEquals(Boolean.FALSE,
			checkIfThereAreChanges.invoke(ecc, (SystemData) null));
	}

	@Test
	public void testCheckIfThereAreChangesInitial()
		throws IllegalArgumentException, IllegalAccessException,
		InvocationTargetException, SecurityException, NoSuchFieldException
	{
		Field fieldData = ElevatorControlCenter.class.getDeclaredField("data");
		fieldData.setAccessible(true);
		fieldData.set(ecc, null);

		assertEquals(Boolean.TRUE,
			checkIfThereAreChanges.invoke(ecc, getDefaultSystemData()));
	}

	@Test
	public void testDoWork() throws Exception
	{
		expect(mock.updateNewTargets(curSystemData, curControlData)).andReturn(
			true);

		SystemData newData = getDefaultSystemData();
		newData.setClockTick(200);
		newData.setFloorHeight(1);
		expect(mock.parseData(curSystemData, curControlData)).andReturn(newData);

		mockObserver.updateNewValues(newData);

		mockAutoAlgorithm.newValuesAvailable(newData, ecc);

		ctrl.replay();

		ecc.registerObserver(mockObserver);

		ecc.doWork();

		ctrl.verify();

		assertEquals(-1, curControlData.get(0).getNewTarget());
		assertEquals(-1, curControlData.get(0).getNewTarget());
	}

	@Test
	public void testDoWorkNoDifference() throws Exception
	{
		expect(mock.updateNewTargets(curSystemData, curControlData)).andReturn(
			true);

		SystemData newData = getDefaultSystemData();
		expect(mock.parseData(curSystemData, curControlData)).andReturn(newData);

		mockAutoAlgorithm.newValuesAvailable(curSystemData, ecc);

		ctrl.replay();

		ecc.registerObserver(mockObserver);

		ecc.doWork();

		ctrl.verify();

		assertEquals(-1, curControlData.get(0).getNewTarget());
		assertEquals(-1, curControlData.get(0).getNewTarget());
	}

	@Test
	public void testStartCanNotInit() throws Exception
	{
		expect(mock.init()).andReturn(false);

		ctrl.replay();

		assertFalse(ecc.start());

		ctrl.verify();
	}


}
